package org.xiaoyu.rpc.registry.nacos;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.common.utils.ConcurrentHashSet;
import lombok.extern.slf4j.Slf4j;
import org.xiaoyu.rpc.common.exception.RpcException;
import org.xiaoyu.rpc.common.url.URL;
import org.xiaoyu.rpc.registry.Discover;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import static org.xiaoyu.rpc.registry.nacos.support.ThrowableConsumer.execute;

/**
 * Created By Have
 * 2021/7/13 16:16
 */
@Slf4j
public class NacosDiscover implements Discover {

    private static final Map<String, List<URL>> SERVICE_CACHE_MAP = new ConcurrentHashMap<>();
    private NacosNamingServiceWrapper namingService;

    public NacosDiscover(NacosNamingServiceWrapper namingService) {
        this.namingService = namingService;
    }

    @Override
    public List<URL> lookupService(URL url) {
        try {
            String lookupServiceName = url.getServiceName();
            System.out.println(lookupServiceName);
            List<URL> urls = SERVICE_CACHE_MAP.get(lookupServiceName);

            if (CollectionUtil.isNotEmpty(urls)) {
                return urls;
            } else {
                List<Instance> allInstance = namingService.getAllInstance(lookupServiceName);
                List<URL> urlList = new ArrayList<>();
                for (Instance instance : allInstance) {
                    Map<String, String> metadata = instance.getMetadata();
                    String serviceName = metadata.get("serviceName");
                    Integer port = Integer.valueOf(metadata.get("port"));
                    String host = metadata.get("host");
                    String group = metadata.get("data");
                    String version = metadata.get("version");
                    String path = metadata.get("path");
                    String weight = metadata.get("weight");
                    URL tempUrl = URL.builder()
                            .path(path)
                            .serviceName(serviceName)
                            .host(host)
                            .port(port)
                            .group(group)
                            .version(version)
                            .weight(Float.valueOf(weight))
                            .build();
                    urlList.add(tempUrl);
                }
//                SERVICE_CACHE_MAP.putIfAbsent(lookupServiceName, urlList);
                addServiceInstanceChangedListener(url);
                return urlList;
            }
        } catch (NacosException e) {
            if (log.isErrorEnabled()) {
                log.info("lookup service fail: ", e);
            }
            throw new RpcException(e.getMessage());
        }
    }

    public void addServiceInstanceChangedListener(URL url) throws NullPointerException, IllegalArgumentException {
        execute(namingService, service -> {
            try {
                service.subscribe(url.getServiceName(), url.getGroup(), e -> {
                    System.out.println("Event happening");
                    if (e instanceof NamingEvent) {
                        NamingEvent namingEvent = (NamingEvent) e;
                        handleEvent(namingEvent);
                    }
                });
            } catch (NacosException e) {
                e.printStackTrace();
            }
        });
    }

    private void handleEvent(NamingEvent namingEvent) {
        String name = namingEvent.getServiceName();

        List<Instance> instances = new ArrayList<>();
        try {
            instances = namingService.getAllInstance(name);
        } catch (NacosException e) {
            e.printStackTrace();
        }
        List<URL> result = new ArrayList<>(instances.size());
        for (Instance instance : instances) {
            Map<String, String> metadata = instance.getMetadata();
            String host = metadata.get("host");
            Integer port = Integer.valueOf(metadata.get("port"));
            String version = metadata.get("version");
            String serviceName = metadata.get("serviceName");
            String path = metadata.get("path");
            Float weight = Float.valueOf(metadata.get("weight"));
            URL url = URL.builder()
                    .path(path)
                    .port(port)
                    .host(host)
                    .weight(weight)
                    .version(version)
                    .serviceName(serviceName)
                    .build();
            result.add(url);
        }
        SERVICE_CACHE_MAP.put(name, result);

    }
}
